﻿/*************************************************************************
* Copyright(c) 2020 - Present ShenZhen EscopeTech

* ShenZhen EscopeTech Incoperated CONFIDENTIAL
* ________________________________________________________________________
*
* [2020] - Present ShenZhen EscopeTech Incoperated
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains
* the property of ShenZhen EscopeTech Incoperated and its suppliers,
* if any. The intellectual and technical concepts contained
* herein are proprietary to ShenZhen EscopeTech Incoperated
* and its suppliers and may be covered by P.R.C. and Foreign Patents,
* patents in process, and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from ShenZhen EscopeTech Incoperated.
*
*************************************************************************/

#include "DUtils.h"

#include <QCryptographicHash>
#include <QDir>
#include <QJsonDocument>
#include <QRegExpValidator>
#include <QUuid>
#include <fstream>

DUtils::DUtils() {}

void DUtils::mergeVecInt(std::vector<int> &vecOld,
                         const std::vector<int> &vecNew)
{
    for (const auto &v : vecNew)
    {
        if (std::none_of(vecOld.begin(), vecOld.end(),
                         [v](int n) { return n == v; }))
        {
            vecOld.push_back(v);
        }
    }
}

void DUtils::mergeVecInt(std::vector<int> &vecOld, int nVecNew)
{
    if (std::none_of(vecOld.begin(), vecOld.end(),
                     [nVecNew](int n) { return n == nVecNew; }))
    {
        vecOld.push_back(nVecNew);
    }
}

bool DUtils::checkIndexRange(const int &nMax, const int &nIndex)
{
    return nIndex >= 0 && nIndex < nMax;
}

QString DUtils::toHexColor(int nR, int nG, int nB)
{
    char sz[100] = {};
    sprintf(sz, "#%.2x%.2x%.2x", nR, nG, nB);
    return QString(sz);
}

void DUtils::setStyleSheet(QWidget *wgt, const QString &strFileName)
{
    const QString strStyle = readFile(strFileName);
    wgt->setStyleSheet(strStyle);

    updateStyle(wgt);
}

void DUtils::clearLayout(QLayout *hLayout)
{
    QLayoutItem *child;
    while ((child = hLayout->takeAt(0)) != 0)
    {
        if (child->widget())
        {
            child->widget()->setParent(NULL);
            delete child->widget();
        }
        else if (child->layout())
        {
            clearLayout(child->layout());
            child->layout()->setParent(NULL);
        }
        delete child;
    }
}

bool DUtils::checkIndexRange(const int &nMax,
                             const std::initializer_list<int> &list)
{
    return std::all_of(list.begin(), list.end(),
                       [&nMax](const int &n)
                       { return checkIndexRange(nMax, n); });
}

std::vector<QFileInfo> DUtils::findFiles(const QString &strDir)
{
    QDir dir(strDir);
    if (!dir.exists())
    {
        return {};
    }

    std::vector<QFileInfo> vecRet;

    dir.setFilter(QDir::Dirs | QDir::Files);
    QFileInfoList list = dir.entryInfoList();
    for (int i = 0; i < list.size(); ++i)
    {
        QFileInfo fileInfo = list.at(i);
        if (fileInfo.fileName() == "." | fileInfo.fileName() == "..")
        {
            continue;
        }
        if (fileInfo.isDir())
        {
            const std::vector<QFileInfo> vecTmp =
                findFiles(fileInfo.filePath());
            vecRet.insert(vecRet.begin(), vecTmp.begin(), vecTmp.end());
        }
        else
        {
            vecRet.push_back(fileInfo);
        }
    }

    return vecRet;
}

void DUtils::makeDir(const QString &strDir) { QDir().mkpath(strDir); }

void DUtils::makeEmptyDir(const QString &strDir)
{
    clearDir(strDir, true);
    makeDir(strDir);
}

void DUtils::makeDir(const std::initializer_list<QString> &listDir)
{
    std::for_each(listDir.begin(), listDir.end(),
                  [](const QString &s) { makeDir(s); });
}

void DUtils::makeDirWithFileName(const QString &strFileName)
{
    const QString strUpDir = getUpDirPath(strFileName);
    if (strUpDir.isEmpty())
    {
        return;
    }

    DUtils::makeDir(strUpDir);
}

std::vector<QFileInfo> DUtils::getFilesWithRegExp(const QString &strFolder,
                                                  const QString &strRegExp)
{
    qInfo() << strFolder << strRegExp;
    std::vector<QFileInfo> vecRet;

    const std::vector<QFileInfo> vecFiles = findFiles(strFolder);

    std::copy_if(vecFiles.begin(), vecFiles.end(), std::back_inserter(vecRet),
                 [strRegExp](const QFileInfo &f)
                 {
                     QRegExp rx(strRegExp);
                     rx.setPatternSyntax(QRegExp::Wildcard);
                     return rx.exactMatch(f.fileName());
                 });

    return vecRet;
}

QStringList DUtils::getFilePathWithRegExp(const QString &strFolder,
                                          const QString &strRegExp)
{
    QStringList listRet;
    std::vector<QFileInfo> listFiles =
        DUtils::getFilesWithRegExp(strFolder, strRegExp);
    std::transform(listFiles.begin(), listFiles.end(),
                   std::back_inserter(listRet),
                   [](const QFileInfo &f) { return f.absoluteFilePath(); });
    return listRet;
}

void DUtils::updateStyle(QWidget *wdg)
{
    QList<QWidget *> vecWdg = wdg->findChildren<QWidget *>();

    wdg->style()->unpolish(wdg);
    wdg->style()->polish(wdg);

    for (int i = 0; i < vecWdg.count(); i++)
    {
        vecWdg.at(i)->style()->unpolish(vecWdg.at(i));
        vecWdg.at(i)->style()->polish(vecWdg.at(i));
    }
    wdg->update();
}

QJsonArray DUtils::readJsonArrayFromFile(const QString &strJsonFile)
{
    QFile file(strJsonFile);
    QByteArray data;
    if (file.open(QIODevice::ReadOnly))
    {
        data = file.readAll();
        file.close();
    }

    QJsonDocument doc(QJsonDocument::fromJson(data));
    return doc.array();
}

QString DUtils::readFile(const QString &strFile)
{
    QString strRet;

    QFile file(strFile);
    if (file.open(QIODevice::ReadOnly))
    {
        strRet = file.readAll();
        file.close();
    }

    return strRet;
}

bool DUtils::removeDir(const QString &path, bool isRecursion)
{
    qInfo() << path;
    bool result = false;
    QDir dir(path);
    if (dir.exists())
    {
        if (isRecursion)
        {
            foreach (QFileInfo info,
                     dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System |
                                           QDir::Hidden | QDir::AllDirs |
                                           QDir::Files,
                                       QDir::DirsFirst))
            {
                if (info.isDir())
                {
                    result = removeDir(info.absoluteFilePath(), isRecursion);
                }
                else
                {
                    result = QFile::remove(info.absoluteFilePath());
                }

                if (!result)
                {
                    return result;
                }
            }
            QDir parentDir(QFileInfo(path).absolutePath());
            result = parentDir.rmdir(path);
        }
        else
        {
            result = dir.rmdir(path);
        }
    }
    return result;
}

bool DUtils::clearDir(const QString &strDirName, bool bRemove)
{
    bool bRet = true;
    QDir dir(strDirName);

    if (dir.exists())
    {
        foreach (
            QFileInfo info,
            dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System |
                                  QDir::Hidden | QDir::AllDirs | QDir::Files,
                              QDir::DirsFirst))
        {
            if (info.isDir())
            {
                bRet = clearDir(info.absoluteFilePath(), true);
            }
            else
            {
                bRet = QFile::remove(info.absoluteFilePath());
            }

            if (!bRet)
            {
                qDebug() << " clearDir failed : " << info.absoluteFilePath();
                return bRet;
            }
        }

        if (bRemove)
        {
            QDir parentDir(QFileInfo(strDirName).absolutePath());
            bRet = parentDir.rmdir(strDirName);
        }
    }

    return bRet;
}

bool DUtils::removeFile(const QString &strFileName)
{
    bool bRet = false;
    do
    {
        if (!QFile::exists(strFileName))
        {
            bRet = true;
            break;
        }

        if (!QFile::setPermissions(strFileName, QFileDevice::WriteOther))
        {
            break;
        }
        if (!QFile::remove(strFileName))
        {
            break;
        }

        bRet = true;
    } while (false);

    return bRet;
}

bool DUtils::copyFilesToDir(const QStringList &listSrc,
                            const QString &strDirDest)
{
    qInfo() << listSrc << strDirDest;
    std::for_each(
        listSrc.begin(), listSrc.end(),
        [&strDirDest](const QString &strFile)
        { copyFile(strFile, strDirDest + "/" + getFileName(strFile)); });
    return true;
}

bool DUtils::copyFilesOfDirectory(const QString &fromDir, const QString &toDir,
                                  bool coverFileIfExist)
{
    qInfo() << fromDir << toDir << coverFileIfExist;

    QDir sourceDir(fromDir);
    QDir targetDir(toDir);
    if (!targetDir.exists())
    {
        if (!targetDir.mkdir(targetDir.absolutePath())) return false;
    }

    QFileInfoList fileInfoList = sourceDir.entryInfoList();
    foreach (QFileInfo fileInfo, fileInfoList)
    {
        if (fileInfo.fileName() == "." || fileInfo.fileName() == "..") continue;

        if (fileInfo.isDir())
        {
            if (!copyFilesOfDirectory(fileInfo.filePath(),
                                      targetDir.filePath(fileInfo.fileName()),
                                      coverFileIfExist))
                return false;
        }
        else
        {
            if (coverFileIfExist && targetDir.exists(fileInfo.fileName()))
            {
                targetDir.remove(fileInfo.fileName());
            }

            if (!QFile::copy(fileInfo.filePath(),
                             targetDir.filePath(fileInfo.fileName())))
            {
                return false;
            }
        }
    }
    return true;
}

bool DUtils::copyFile(const QString &strSrc, const QString &strDest)
{
    bool bRet = false;
    do
    {
        if (!QFile::exists(strSrc))
        {
            break;
        }

        DUtils::removeFile(strDest);

        if (!QFile::setPermissions(strSrc, QFileDevice::WriteOther))
        {
            break;
        }
        if (!QFile::copy(strSrc, strDest))
        {
            break;
        }

        bRet = true;
    } while (false);

    return bRet;
}

QString DUtils::getFileName(const QString &strFile)
{
    qInfo() << strFile;
    QString strRet = strFile;
    strRet.replace("\\", "/");
    int nPos = strFile.lastIndexOf("/");
    if (nPos == -1)
    {
        return QString();
    }

    qInfo() << strRet << nPos;
    strRet.remove(0, nPos + 1);
    return strRet;
}

QString DUtils::getUpDirName(const QString &strFile)
{
    qInfo() << strFile;
    QString strRet = strFile;
    strRet.replace("\\", "/");
    int nPos = strRet.lastIndexOf("/");
    if (nPos == -1)
    {
        return QString();
    }
    strRet = strRet.left(nPos);

    nPos = strRet.lastIndexOf("/");
    if (nPos == -1)
    {
        return QString();
    }

    return strRet.right(strRet.length() - nPos - 1);
}

QString DUtils::getUpDirPath(const QString &strFile)
{
    qInfo() << strFile;
    QString strRet = strFile;
    strRet.replace("\\", "/");
    int nPos = strRet.lastIndexOf("/");
    if (nPos == -1)
    {
        return QString();
    }

    return strRet.left(nPos);
}

QJsonObject DUtils::stringtoObject(const QString &strJson)
{
    const QJsonDocument doc = QJsonDocument::fromJson(strJson.toUtf8());
    return doc.object();
}

QJsonArray DUtils::stringToArray(const QString &strJson)
{
    const QJsonDocument doc = QJsonDocument::fromJson(strJson.toUtf8());
    return doc.array();
}

QString DUtils::jsonToString(const QJsonObject &json)
{
    QJsonDocument doc(json);
    return doc.toJson();
}

QJsonObject DUtils::readJsonFromFile(const QString &strJsonFile)
{
    qInfo() << strJsonFile;
    QFile file(strJsonFile);
    QByteArray data;
    if (file.open(QIODevice::ReadOnly))
    {
        data = file.readAll();
        file.close();
    }

    QJsonDocument doc(QJsonDocument::fromJson(data));
    return doc.object();
}

void DUtils::writeJsonToFile(const QString &strJsonFile,
                             const QJsonObject &jsonData)
{
    QJsonDocument doc(jsonData);
    QFile file(strJsonFile);
    if (file.open(QIODevice::WriteOnly))
    {
        file.write(doc.toJson());
        file.close();
    }
}

void DUtils::writeJsonArrayToFile(const QString &strJsonFile,
                                  const QJsonArray &jarData)
{
    QJsonDocument doc(jarData);
    QFile file(strJsonFile);
    if (file.open(QIODevice::WriteOnly))
    {
        file.write(doc.toJson());
        file.close();
    }
}

QString DUtils::createNewUUID()
{
    const QUuid id = QUuid::createUuid();
    QString strId = id.toString();
    strId = strId.replace("-", "");
    strId = strId.replace("{", "");
    strId = strId.replace("}", "");
    return strId;
}

QJsonValue DUtils::jsonGetValue(const QJsonObject &json, const QString &strPath)
{
    QJsonValue jsonVRet;

    QStringList lstKey = strPath.split("/", QString::SkipEmptyParts);
    if (lstKey.size() == 0)
    {
        return jsonVRet;
    }

    QJsonObject jsonTemp = json;
    bool bFindAll = true;
    for (int i = 0; i < lstKey.size() - 1; i++)
    {
        QString strKey = lstKey[i];
        if (!jsonTemp.contains(strKey))
        {
            bFindAll = false;
            break;
        }
        jsonTemp = jsonTemp.value(strKey).toObject();
    }

    if (bFindAll)
    {
        if (jsonTemp.contains(lstKey.last()))
        {
            jsonVRet = jsonTemp.value(lstKey.last());
        }
    }

    return jsonVRet;
}
